package wortubung.lang.de;

import static wortubung.lang.LanguageManager.*;
import static wortubung.lang.de.DeutschSpracheWerkzeug.*;
import static wortubung.util.CommonUtils.*;

import java.io.IOException;
import java.util.Properties;

import org.json.JSONArray;

import wortubung.core.LANG;
import wortubung.core.POS;
import wortubung.inflection.CONJUGATION;
import wortubung.inflection.DerivedVerbConjugation;
import wortubung.inflection.PrefixMatcher;
import wortubung.inflection.PrefixType;
import wortubung.inflection.UnknownConjugationException;
import wortubung.inflection.VerbConjugation;
import wortubung.lang.ConjugatorBase;
import wortubung.util.ImmutableStringList;
import wortubung.util.LatinScript;

// http://conjugator.reverso.net/conjugation-german-verb-<THE_VERB>.html
public class Konjugierer extends ConjugatorBase {

	static final ImmutableStringList PRESENT_EN_INDICES   = isl("e",  "st",   "t",  "en",  "t",   "en" );
	static final ImmutableStringList PRESENT_N_INDICES    = isl("e",  "st",   "t",  "n",   "t",   "n"  );
	static final ImmutableStringList SIMPLE_PAST_INDICES  = isl("te", "test", "te", "ten", "tet", "ten");
	static final ImmutableStringList SUBJ_PRESENT_INDICES = isl("e",  "est",  "e",  "en",  "et",  "en" );

	public static final Konjugierer defaultKonjugierer = new Konjugierer();

	static {
		defaultKonjugierer.readInitFiles(null, DE_KONJUGATIONEN_AS_IS_FILE);
	}

	public static VerbConjugation conjugate(String verb) { return defaultKonjugierer.getConjugation(verb); }

	@Override public LANG getLanguage() { return LANG.de; }

	@Override
	protected void furtherInit(Properties props) throws IOException {
		// Read irregulars by-rule from the resource (JSON) file
		String jsonStr = readFileFromCPAsString(DE_KONJUGATIONEN_BY_RULE_FILE);
		JSONArray jsonArr = new JSONArray(jsonStr);
		for (int i=0; i<jsonArr.length(); ++i)
			addIrregulerConjugation(StarkeVerbKonjugierer.getRuleFromJSON(jsonArr.getJSONObject(i)));
	}

	@Override
	public VerbConjugation getConjugation(String verb) {
		// For instance, "fehlen" as a verb is regular, but
		// as suffix in "befehlen" and "empfehlen" is irregular.
		return getConjugation_(verb, false);
	}
	
	private VerbConjugation getConjugation_(String verbOfSuffix, boolean suffix) {
		if (!verbOfSuffix.endsWith("n"))
			UnknownConjugationException.badVerb(verbOfSuffix);

		VerbConjugation vc = getIrregulerConjugator(verbOfSuffix);
		if (vc != null && (suffix == vc.isSuffixOnly()))
			return vc.getConjugationResult(null);

		VerbConjugation ret = checkPrefix(verbOfSuffix);
		if (ret != null)
			return ret;

		String[] prefixSuffix = getPrefixSuffixForIrregular(verbOfSuffix);
		vc = getIrregulerConjugator(prefixSuffix[1]);
		if (vc == null || vc.isSuffixOnly())
			return defaultKonjugierer.conjugate(prefixSuffix[0], prefixSuffix[1]);
		if (prefixSuffix[0] == null)
			return vc;
		return new DerivedVerbConjugation(prefixSuffix[0], vc);
	}

	private VorangestelltenVerbKonjugation checkPrefix(String verb) {
		String[] prefixSuffix;

		// Check for INSEPARABLy prefixed verbs
		int prefixIndex = verb.indexOf("+");
		if (prefixIndex > 0) {
			prefixSuffix = verb.split("\\+");
			return new VorangestelltenVerbKonjugation(prefixSuffix[0], false, getConjugation(prefixSuffix[1]));
		}

		// Check for SEPARABLy prefixed verbs
		prefixIndex = verb.indexOf("|");
		if (prefixIndex > 0) {
			prefixSuffix = verb.split("\\|");
			return new VorangestelltenVerbKonjugation(prefixSuffix[0], true, getConjugation(prefixSuffix[1]));
		}

		// Check for prefix that is not explicitly marked separable or otherwise.
		PrefixMatcher.Match match = DeutschSpracheWerkzeug.verbPrefixMatcher.match(verb);
		if (match != null) {
			if (match.type == PrefixType.GERMAN_DUAL)
				UnknownConjugationException.badVerb(verb + ": must explicitly specify the prefix to be separable or not.");
			return new VorangestelltenVerbKonjugation(match.prefix, match.isSeparable(), getConjugation_(match.rest, true));
		}

		return null;
	}

	@Override
	protected String[] conjugate(String verbSuffix, CONJUGATION type, boolean isSuffix) {
		ImmutableStringList indices = null;
		String root = getVerbStem(verbSuffix);
		boolean en = (root.length() == verbSuffix.length() - 2);
		switch (type) {
		case PRESENT:      return conjugatePresent(root, en ? PRESENT_EN_INDICES : PRESENT_N_INDICES);
		case SIMPLE_PAST:
		case SUBJ_PAST:    indices = SIMPLE_PAST_INDICES; break;
		case SUBJ_PRESENT: indices = SUBJ_PRESENT_INDICES; break;
		case IMPERATIVE:   return imperatif(verbSuffix);
		default:           return null;
		}

		return appendWithExtraE(root, indices, POS.VERB);
	}

	private static String[] conjugatePresent(String root, ImmutableStringList indices) {
		String[] ret = appendWithExtraE(root, indices, POS.VERB);
		String s2index = indices.get(1);
		if (root.endsWith("s") && s2index.startsWith("s"))
			ret[1] = root + s2index.substring(1); // remove extra 's'
		return ret;
	}

	protected String[] imperatif(String verbSuffix) {
		String[] result = conjugate(verbSuffix, CONJUGATION.PRESENT, false);
		if (result != null) {
			result[1] = imperatif_du(verbSuffix);
    		result[0] = result[2] = null;
    		result[3] = result[5];
		}
		return result;
	}

	// http://joycep.myweb.port.ac.uk/verbs/imperat.html
	//
	// 1. The "du" form of the imperative is formed by dropping the final "-en" of the infinitive and
	//    adding "-e" to the end of the word.
	//    This "-e" ending is usually dropped in spoken and quite often in written German as well.
	// 2. The "-e" ending on the "du" imperative is always kept with verbs whose stem end in
	//    "-d", "-t", "-ig" and "-m" or "-n" after another consonant. Example: "warten" (to wait).
	// 3. Verbs whose infinitive ends in "-eln" drop the "-e" of the stem in the "du" imperative
	//    but not in the "ihr" and "Sie" forms. Example: "handeln" (to act).
	private static String imperatif_du(String verb) {
		if (verb.endsWith("eln"))
			return chopRight(verb, 3) + "le";
		String root = getVerbStem(verb);
		char ch = last(root);
		boolean eAlways = false;
		switch(ch) {
		case 'g': if (!root.endsWith("ig")) break;
		case 'd':
		case 't':
		case 'm': eAlways = true; break;
		case 'n': eAlways = !LatinScript.isVowel(root.charAt(root.length()-2)); break;
		}
		return root + (eAlways ? "e" : "(e)");
	}

	protected String participle(String verbSuffix, boolean isPresent) {
		if (isPresent)
			return verbSuffix + 'd';

		// Weak verb past participle
		String root = chopRight(verbSuffix, verbSuffix.endsWith("en") ? 2 : 1);
		root = appendWithExtraE(root, "t", POS.VERB);
		if (verbSuffix.endsWith("ieren"))
			return root;
		return "ge" + root;
	}
}
